iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0

很多語言或是套件都會有生命週期的概念,就像是之前介紹的git hook一樣,React當然也不例外,供我們在特定階段能做一些操作。

先看常見的生命週期(如下圖)。
https://ithelp.ithome.com.tw/upload/images/20230930/20162751zPxIVxrmQa.png

這些都是class component的生命週期,會從建立元件開始到移除依序執行這些函數,如下

class App extends Component {
	constructor(props) {
		super(props);
	}
  componentDidMount() {}
  componentDidUpdate(prevProps, prevState, snapshot){}
	render() {
		return <>
			<h1>生命週期</h1>;
		<>
	}
}

圖垂直來看可以分為兩種階段,Render和Commit這在之前也有介紹到,忘記的可以再回顧一下。這邊Render階段指的副作用會像是呼叫API等的操作,不可以在這個階段使用。

水平維度可以根據不同時期分為三個階段

  • Mounting:component被建立且被放到畫面上的階段

    1. constructor

      當我們建立類別物件實體的時候一開始執行的函式,在這個地方會宣告元件有哪些狀態等。

    2. render

      看上面的例子是不是覺得很眼熟,就是之前function component裡面return的JSX,用來呈現畫面的函式。

    3. componentDidMount

      當HTML第一次被畫到頁面上後會呼叫的函式,可以用來呼叫API資料或抓取HTML Tag等。

  • Updating:畫面更新的階段

    1. Render

      根據更新的狀態資料在執行一次Render

    2. componentDidUpdate

      更新完畫面後就會呼叫

  • Unmounting:component被從畫面移除時會呼叫

    1. componentWillUnmount

      component要被移除的時候,可以用來移除綁定監聽的事件,避免組件消失後監聽還在。

Function component生命週期

我們也可以用hook來讓function component有生命週期的效果,主要就是使用useEffect這個hook。

useEffect(setup, dependencies?)

setup是要執行的內容

dependencies是相依的變數,若是放在這的變數有改變就會執行setup

如果dependencies是空陣列,只會執行第一次,若整個沒有放就會每次渲染都執行。

function App() {
	const [val, setVal] = useState(0);
	useEffect(() => {
		// **componentDidMount或componentDidUpdate
		return () => {
			// componentDidUpdate
			//** componentWillUnmount
		**}**
	}, []);
}

export default App;

這邊有個可能常被忽略的是,如果是更新狀態而執行useEffect的話,會先執行下圖的1再執行2喔!
https://ithelp.ithome.com.tw/upload/images/20230930/20162751mbLCq8OtwG.png

不常用生命週期

https://ithelp.ithome.com.tw/upload/images/20230930/20162751xbt6nLlmGS.png

再來要來講不常用的幾個生命週期

  • getDerivedStateFromProps(props, state)

    回傳要更新的state,不更新回傳null

  • shouldComponentUpdate(nextProps, nextState)

    用於控制component是否要更新,預設每次都更新(回傳true),可以判斷props不同時才更新,來減少不必要的更新達到優化效能,但是,通常也可以用內建的PureComponent(有實作shouldComponentUpdate)來達成效果。

    看個官方PureComponent的例子,在這個例子Greeting component的props只有name,所以只有在name有變更時這個component再更新這樣會比較節省資源,

    import { PureComponent, useState } from 'react';
    
    class Greeting extends PureComponent {
      render() {
        console.log("Greeting was rendered at", new Date().toLocaleTimeString());
        return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>;
      }
    }
    
    export default function MyApp() {
      const [name, setName] = useState('');
      const [address, setAddress] = useState('');
      return (
        <>
          <label>
            Name{': '}
            <input value={name} onChange={e => setName(e.target.value)} />
          </label>
          <label>
            Address{': '}
            <input value={address} onChange={e => setAddress(e.target.value)} />
          </label>
          <Greeting name={name} />
        </>
      );
    }
    

    如果改為我們自己實作shouldComponentUpdate可以改為如下,判斷新的name和原本的name如果有不同才更新

    import { Component, useState } from 'react';
    
    class Greeting extends Component {
      shouldComponentUpdate(nextProps) {
        return this.props.name !== nextProps.name
      }
      render() {
        console.log("Greeting was rendered at", new Date().toLocaleTimeString());
        return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>;
      }
    }
    
    export default function MyApp() {
      const [name, setName] = useState('');
      const [address, setAddress] = useState('');
      return (
        <>
          <label>
            Name{': '}
            <input value={name} onChange={e => setName(e.target.value)} />
          </label>
          <label>
            Address{': '}
            <input value={address} onChange={e => setAddress(e.target.value)} />
          </label>
          <Greeting name={name} />
        </>
      );
    }
    

但是官方文件也是有提到,如果你不是維護舊專案建議還是使用function component來寫React會比較好。所以同樣的功能當然也會有function component版本的,就是memo。只要把memo帶入一個function component就可以了,然後這個memo函式會回傳一個component。

import { memo} from 'react';

const Greeting = memo(function Greeting({ name }) {
  console.log("Greeting was rendered at", new Date().toLocaleTimeString());
  return <h3>Hello{name && ', '}{name}!</h3>;
});
  • getSnapshotBeforeUpdate(prevProps, prevState)

    可以用來取得畫面更新前的DOM的資訊(滾軸位置)。回傳值會傳給componentDidUpdate的第3個參數。

參考

https://zh-hant.legacy.reactjs.org/docs/react-component.html#shouldcomponentupdate

https://react.dev/reference/react/PureComponent


上一篇
淺談React的Virtual DOM機制
下一篇
React中的useReducer Hook:簡單解釋和範例
系列文
前端開發之那些我會的與我不會的技術31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言